查看原文
其他

网易云课堂Excel课程爬虫思路

2017-09-17 杜雨 数据小魔方

由于即将毕业,马上进入职场,想来是时候需要巩固一下基本职场技能了,特别是Excel这种杀手级职场应用。


可是如今网络这么发达,到处都充斥着Excel课程、视频、教程,真的很容易让人眼花缭乱,不知所措。


看书来的太慢了,还是直接看视频吧,简单粗暴,学习之前总要熟悉一下Excel教学行业的大致情况吧,今天就拿网易云课堂的Excel板块作为目标,在练习数据爬取的同时,顺便了解一下Excel培训行业的行情,知己知彼才能百战不殆,才能更加集中精力的学习那些精品课程。


url<-"http://study.163.com/category/excel#/"      

#Excel板块首页网址

url<-"http://study.163.com/category/excel#/?p=2"  

#加载第二页之后的网址


网易云课堂的网页不是很复杂,而且URL是很规律的参数拼接,反倒最底部可以看到,它是点击翻页,一共只有9页,而且页面是顺序加载,OK,可以直接手动拼接遍历网址了。


url<-paste0("http://study.163.com/category/excel#/?p=",1:9)


library("rvest")

library("XML")

library("RCurl")

postForm(url[1],)

web<-read_html(url[1],encoding="UTF-8")%>% 

html_nodes("div.uc-ykt-coursecard-wrap_tit > h3") %>% html_text()


but以上尝试都失败了!


当我想当然的以为网易云课堂用R可以轻松搞定的时候,猛然发现他用的XHR技术,奔溃……


首先我们再次分析网页,打开云课堂Excel模块首页,按F12翻到XHR菜单



在底部你可以看到studycourse.json文件,点开之后可以看到右侧的Preview栏目里面全部都是课程信息,是一个json文件。



这个模块是Chrome的开发者工具后台,就是我们常说的抓包工具,现在切换到Headers栏目,可以看到云课堂所有的课程信息都是在一个.josn网页里面存放着,这里便是阻碍我们使用普通方法爬取数据的困难之源。



仔细看你会发现General里面用到的Request Method 是Post,Post方法在 提交网址和参数的同时,要提交表单数据,这时候我们需要详细的查看Request Headers里面的参数信息。



因为POST方法涉及到传递表单参数,所以构造报头一定要添加Content-Type参数,这里的Content-Type参数是application/json,需要传递json字符串。


看来今天这个案例用R语言有些困哪了(使用 webdriver除了偷懒,并不能锻炼你什么能力),本案例POST要传递json表单参数,R里面没有很多的处理json的方式,再加上RCurl里面的POST方法资料太少,没有什么可参考资料。(还是R语言的爬虫生态太弱了)。


所以今天用Pyhton来演示本案例:


import json

import requests

import pandas as pd

import os


第一步:分析XHR中POST方法的表单规律:


使用POST方法爬取数据,需要重点关注Request Headers中的User-Agent、Content-Type以及最后面的表单体。


本例的网页,虽然看上去分了9个子页面,但是实际上其调用的json仅有一个主页:http://study.163.com/p/search/studycourse.json


而不同子页面主要是用过表单体中的参数来确定到的。


{"pageIndex":1,"pageSize":50,"relativeOffset":0,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}

{"pageIndex":2,"pageSize":50,"relativeOffset":50,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}

{"pageIndex":3,"pageSize":50,"relativeOffset":100,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}

……

{"pageIndex":9,"pageSize":50,"relativeOffset":400,"frontCategoryId":"400000000149040","searchTimeType":-1,"orderType":0,"priceType":-1,"activityId":0}



以上我给出了9个页面的表单体信息中的前三个和最后一个,通过找规律你会发现,差异仅在pageIndex和relativeOffset参数上,其他参数都是一样的。pageIndex和relativeOffset分别代表页面id和主页中信息条目的偏移量。偏移量间隔50,也就是我们在网页上看到的单页展示课程数目。



构造表单体:


payload = {

           "pageIndex":1,

           "pageSize":50,

           "relativeOffset":100,

           "frontCategoryId":"400000000149040"

          }


一共9个子页面,通过表单体参数进行页面遍历控制


构造报头信息:

headers = {

          'Accept':'application/json',

          'content-type':'application/json',

          'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'

          }



第二步:构造网页爬取函数:


url = 'http://study.163.com/p/search/studycourse.json'

r=requests.post(url,data=json.dumps(payload),headers=headers);r


<Response [200]>


状态码OK!


content=r.json();content



type(content)

dict  ###返回内容类型为字典


返回的是一个字典,里面嵌套有很多层,仔细观察你会先发,我们需要的内容都存放在content['result']['list']里面


content['result']['list']   



type(content['result']['list'])

list   ###返回类型是列表


OK,构造一个循环,将每一次请求返回提取的内容拼接在一个列表里面:


fullinfo=[]

for i in list(range(1,10)):

    payload['pageIndex']=i

    payload['relativeOffset']=50*i-50

    r=requests.post(url,data=json.dumps(payload),headers=headers)

    content=r.json()

    fullinfo=fullinfo+content['result']['list']

    print("第{}部分已加载".format(i))


df1=pd.DataFrame(fullinfo)

df1.columns    ###返回所有列信息

Index(['activityIds', 'bigImgUrl', 'courseCardProps', 'courseId',

       'description', 'discountPrice', 'discountRate', 'displayType',

       'endTime', 'forumTagLector', 'gmtModified', 'imgUrl', 'isPromStatus',

       'learnerCount', 'lectorName', 'originalPrice', 'productId',

       'productName', 'productType', 'provider', 'published',

       'schoolShortName', 'score', 'scoreLevel', 'startTime', 'tagIap',

       'tagLectorTime'],

      dtype='object')


###提取我们需要的列:

mydata=df1[["productName","discountPrice","discountRate","lectorName","originalPrice","description","provider","score","scoreLevel"]]



存储本地:

os.chdir('D:/Python/Data')

mydata.to_csv('yunketang.csv',index=False)


存储到本地硬盘,搞完收工!一共421条Excel课程信息,和后台的信息一致。



下一篇针对这一次爬虫结果做可视化分析!



在线课程请点击文末阅读原文:




您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存